/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkConstantBoundaryCondition.h"
#include "itkPadImageFilter.h"

int itkPadImageFilterTest( int, char* [] )
{
  using ShortImage = itk::Image< short, 2 >;
  using SizeType = ShortImage::SizeType;
  using SizeValueType = ShortImage::SizeValueType;

  using FilterType = itk::PadImageFilter< ShortImage, ShortImage >;
  FilterType::Pointer padFilter = FilterType::New();

  SizeType lowerBound = {{1, 2}};
  SizeType upperBound = {{3, 4}};
  SizeValueType lowerBoundArray[2] = {1, 2};
  SizeValueType upperBoundArray[2] = {3, 4};

  padFilter->SetPadLowerBound( lowerBound );
  if ( padFilter->GetPadLowerBound() != lowerBound )
    {
    std::cerr << "Lower bound not recovered while testing SetLowerBound(SizeType). Got "
              << padFilter->GetPadLowerBound() << ", expected " << lowerBound << std::endl;
    return EXIT_FAILURE;
    }

  padFilter->SetPadLowerBound( lowerBoundArray );
  if ( padFilter->GetPadLowerBound() != lowerBound )
    {
    std::cerr << "Lower bound not recovered while testing SetLowerBound(SizeValueType[]). Got "
              << padFilter->GetPadLowerBound() << ", expected " << lowerBound << std::endl;
    return EXIT_FAILURE;
    }

  padFilter->SetPadUpperBound( upperBound );
  if ( padFilter->GetPadUpperBound() != upperBound )
    {
    std::cerr << "Upper bound not recovered while testing SetUpperBound(SizeType). Got "
              << padFilter->GetPadUpperBound() << ", expected " << upperBound << std::endl;
    return EXIT_FAILURE;
    }

  padFilter->SetPadUpperBound( upperBoundArray );
  if ( padFilter->GetPadUpperBound() != upperBound )
    {
    std::cerr << "Upper bound not recovered while testing SetUpperBound(SizeValueType[]). Got "
              << padFilter->GetPadUpperBound() << ", expected " << upperBound << std::endl;
    return EXIT_FAILURE;
    }

  padFilter->SetPadBound( lowerBound );
  if ( padFilter->GetPadLowerBound() != lowerBound )
    {
    std::cerr << "Lower bound not recovered while testing SetPadBound(). Got "
              << padFilter->GetPadLowerBound() << ", expected " << lowerBound << std::endl;
    return EXIT_FAILURE;
    }

  if ( padFilter->GetPadUpperBound() != lowerBound )
    {
    std::cerr << "Upper bound not recovered while testing SetPadBound(). Got "
              << padFilter->GetPadUpperBound() << ", expected " << lowerBound << std::endl;
    return EXIT_FAILURE;
    }

  if ( padFilter->GetBoundaryCondition() != nullptr )
    {
    std::cerr << "Default BoundaryCondition was not nullptr." << std::endl;
    return EXIT_FAILURE;
    }

  itk::ConstantBoundaryCondition< ShortImage > bc;
  bc.SetConstant( 13 );

  padFilter->SetBoundaryCondition( &bc );
  if ( padFilter->GetBoundaryCondition() != &bc )
    {
    std::cerr << "Set/GetBoundaryCondition test failed." << std::endl;
    return EXIT_FAILURE;
    }

  // Fill in a test image
  ShortImage::Pointer    inputImage = ShortImage::New();
  ShortImage::SizeType   inputSize  = {{1, 1}};
  ShortImage::RegionType inputRegion( inputSize );
  inputImage->SetRegions( inputRegion );
  inputImage->Allocate();
  inputImage->FillBuffer( 1 );

  lowerBound.Fill( 2 );
  padFilter->SetPadBound( lowerBound );

  padFilter->SetInput( inputImage );
  padFilter->UpdateLargestPossibleRegion();

  // Checkout output values
  ShortImage::Pointer output = padFilter->GetOutput();
  for (int j = -2; j <= 2; j++)
    {
    for (int i = -2; i <= 2; i++)
      {
      ShortImage::IndexType index;
      index[0] = i; index[1] = j;

      short pixel = output->GetPixel( index );
      std::cout << pixel << " ";
      if ( index[0] == 0 && index[1] == 0 )
        {
        if ( pixel != 1 )
          {
          std::cerr << "Center pixel has unexpected value " << pixel
                    << ", expected " << 1 << std::endl;
          return EXIT_FAILURE;
          }
        }
      else
        {
        if ( pixel != 13 )
          {
          std::cerr << "Padded pixel at index " << index << " has unexpected value "
                    << pixel << ", expected " << 13 << std::endl;
          return EXIT_FAILURE;
          }
        }
      }
    std::cout << std::endl;
    }

  // Set the boundary condition back to nullptr and check that exception
  // is thrown.
  std::cout << "Testing that exception is thrown when boundary condition is nullptr." << std::endl;
  padFilter->SetBoundaryCondition( nullptr );
  try
    {
    padFilter->Update();
    std::cerr << "Failed to catch expected exception when boundary condition is nullptr." << std::endl;
    return EXIT_FAILURE;
    }
  catch ( itk::ExceptionObject & e )
    {
    std::cout << "Caught expected exception when boundary condition is nullptr." << std::endl;
    std::cout << e << std::endl;
    }
  catch (...)
    {
    std::cerr << "Exception caught, but not the expected kind." << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}
